//write.cpp
//author: aandriyash

#include "utils.h"

#include <stdio.h>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>

#include <iostream>

int WriteToRAID01(
    FILE* pFile
    , int fileSize
    , int blockNumber
    , int totalDisks
    , int totalBlocks
    , int blockSize
)
{
    RETURN_IF_ERROR(Mapping(totalDisks, totalBlocks, blockSize, false));

    int fileBlockSize = fileSize*2 / totalDisks; 
    int offset = (blockNumber - 1) * blockSize;

    for (int i = 1; i < (totalDisks/2) + 1; i++) 
    {
        if (totalDisks/2 == i) 
        {
            fileBlockSize += (fileSize % (totalDisks /2));
        }
        fread(ppMappedDataArray[i] + offset, 1, fileBlockSize, pFile);
        memset(ppMappedDataArray[i] + offset + fileBlockSize, 0, blockSize - fileBlockSize);
    }
    
    //mirroring
    for(int i = (totalDisks / 2) + 1; i < totalDisks + 1; i++) 
    {
        memcpy(ppMappedDataArray[i] + offset, ppMappedDataArray[i - (totalDisks / 2)] + offset, blockSize);
    }
    
    RETURN_IF_ERROR(Unmapping(totalDisks, totalBlocks, blockSize, false));
    
    return 0;
}

int WriteToRAID10(
    FILE* pFile
    , int fileSize
    , int blockNumber
    , int totalDisks
    , int totalBlocks
    , int blockSize
)
{
    RETURN_IF_ERROR(Mapping(totalDisks, totalBlocks, blockSize, false));

    int fileBlockSize = (fileSize*2 )/ (totalDisks); //parts of pFile for writing to blocks;
    int offset = (blockNumber - 1) * blockSize;
    
    for (int i = 1; i < totalDisks; i += 2) {
        if (i == (totalDisks - 1))
        {
            fileBlockSize += (fileSize % (totalDisks / 2));
        }
        //write file to mappedData with offset
        fread(ppMappedDataArray[i] + offset, 1, fileBlockSize, pFile);
        //filling the rest of block with '0'
        memset(ppMappedDataArray[i] + offset + fileBlockSize, 0, blockSize - fileBlockSize);
    }
    
    for (int i = 2; i < totalDisks + 1; i += 2) 
    {
        memcpy(ppMappedDataArray[i] + offset, ppMappedDataArray[i - 1] + offset, blockSize);
    }
    
    RETURN_IF_ERROR(Unmapping(totalDisks, totalBlocks, blockSize, false));
    
    return 0;
}

int main(int argc, char* argv[]) {

    if (argc < 3) 
    {
        std::cout << "Not enough parameters\n";
        return 1;
    }

    RaidConfig raidConf;
    raidConf.Read();     
    
    RETURN_IF_ERROR_TRACE((IsDiskCrashed(raidConf.totalDisks, false)), ("Rebuild RAID.\n"));

    int blockNumber = atoi(argv[1]);
    if ((blockNumber == 0) || (blockNumber > raidConf.totalBlocks)) 
    {
        std::cout << "Number of blocks must be 1.." << raidConf.totalBlocks << "\n";
        return 1;
    }

    char* pFileName = argv[2];

    struct stat file_stat;
    
    int statf = stat(pFileName, &file_stat);
    
    if (0 != statf) 
    {
        std::cout << "Can't get statistics of pFile " << pFileName << ".\n";
        return 1;
    }
    
    if (file_stat.st_size > raidConf.blockSize) 
    {
        std::cout << "Size of pFile must be <= " << raidConf.blockSize << "\n";
        return 1;
    }

    FILE * pFile = fopen(pFileName, "rb+");
    if (NULL == pFile) 
    {
        std::cout << "Can't open pFile " << pFile << "\n";
        return 1;
    }
    int err = 0;
    switch (raidConf.raidLevel) 
    {
    case 1:
        err = WriteToRAID01(
                    pFile
                    , file_stat.st_size
                    , blockNumber
                    , raidConf.totalDisks
                    , raidConf.totalBlocks
                    , raidConf.blockSize
                );
        break;
    case 10:
        err = WriteToRAID10(
                    pFile
                    , file_stat.st_size
                    , blockNumber
                    , raidConf.totalDisks
                    , raidConf.totalBlocks
                    , raidConf.blockSize
                );
        break;
    default:
        err = 1;
        break;
    }
    
    fclose(pFile);
    return err;
}

